package com.fintech.pangu.security;

import com.fintech.pangu.security.authorize.AuthorizeConfigManager;
import com.fintech.pangu.security.config.PanGuAuthenticationManagerBuilderManager;
import com.fintech.pangu.security.config.PanGuHttpSecurityManager;
import com.fintech.pangu.security.web.UnauthorizedEntryPoint;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.cors.CorsConfigurationSource;

import java.util.List;

/**
 * PanGu安全基类
 * 只有安全基类继承Spring WebSecurityConfigurerAdapter，安全基类封装安全相关的公共/可配置逻辑
 * 并留给子类custom自定义方法
 * 1、公共/可配置逻辑在安全基类实现并提供配置项
 * 2、认证逻辑通过继承安全基类，custom扩展
 * 3、鉴权逻辑实现AuthorizeConfigProvider接口，由安全基类通过AuthorizeConfigManager统一管理注入
 */
public abstract class PanGuWebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    /**
     * 盘古微服务HttpSecurity相关配置管理器
     */
    private PanGuHttpSecurityManager panGuHttpSecurityManager;

    /**
     * 盘古微服务授权配置管理器
     */
    private AuthorizeConfigManager authorizeConfigManager;

    /**
     * 盘古微服务AuthenticationManagerBuilder相关配置管理器
     */
    private PanGuAuthenticationManagerBuilderManager panGuAuthenticationManagerBuilderManager;

    /**
     * 认证管理器
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /**
         * 1、抽取 公共逻辑/可配置逻辑
         */
        // 移动到子类实现，否则当没有自定义userDetailsService()，且轮到DaoAuthenticationProvider执行认证时会循环调用
        //auth.userDetailsService(userDetailsService()).passwordEncoder(getPasswordEncoder());

        /**
         * 2、调用自定义认证管理器方法
         */
        customAuthenticationManagerBuilder(auth);

        /**
         * 3、通过AuthenticationManagerBuilder配置管理器将AuthenticationManagerBuilder传入，用于各个子配置
         */
        getPanGuAuthenticationManagerBuilderManager().config(auth);
    }

    /**
     * 留给子类实现的自定义认证处理器方法
     * @param auth
     * @throws Exception
     */
    protected void customAuthenticationManagerBuilder(AuthenticationManagerBuilder auth) throws Exception {
    }


    /**
     * WebSecurity
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        /**
         * 1、抽取 公共逻辑/可配置逻辑
         */
        // 配置web.ignoring()
        List<String> ignoringUrls = ignoringUrls();
        if(ignoringUrls!=null && ignoringUrls.size()>0){
            for(String ignoringUrl : ignoringUrls){
                if(StringUtils.hasText(ignoringUrl)){
                    web.ignoring().antMatchers(ignoringUrl);
                }
            }
        }

        /**
         * 2、调用自定义WebSecurity方法
         */
        customWebSecurity(web);
    }


    /**
     * 配置完全不经过spring security filter的路径列表
     * 子类实现，默认是空集合
     * @return
     */
    protected abstract List<String> ignoringUrls();


    /**
     * 留给子类实现的自定义WebSecurity方法
     * @param web
     * @throws Exception
     */
    protected void customWebSecurity(WebSecurity web) throws Exception {
    }




    /**
     * HttpSecurity
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 1、抽取的 公共逻辑/可配置逻辑
         */
        // csrf (csrf默认开启)
        if(!enableCsrf()){
            http.csrf().disable();
        }

        // cors配置
        if(enableCors()){
            http.cors().configurationSource(corsConfigurationSource());
        }

        // headers frameOptions配置，http默认加载headers子配置-->frameOptions子配置，frameOptions默认开启
        if(!enableFrameOptions()){
            http.headers().frameOptions().disable();
        }

        // 是否无状态
        if(isStateless()){
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }

        // 为Ajax类型请求指定未授权时的公共EntryPoint
        addAjaxCommonAuthenticationEntryPoint(http);


        /**
         * 2、调用自定义HttpSecurity方法，主要是认证相关 和 没有提取出来的的安全配置
         */
        customHttpSecurity(http);


        /**
         * 3、通过授权配置管理器加载所有AuthorizeConfigProvider
         */
        getAuthorizeConfigManager().config(http.authorizeRequests());


        /**
         * 4、通过HttpSecurity子配置管理器将httpSecurity传入，用于各个子配置
         */
        getPanGuHttpSecurityManager().config(http);
    }


    /**
     * 为Ajax类型请求指定未授权时的公共EntryPoint
     * @param http
     * @throws Exception
     */
    private void addAjaxCommonAuthenticationEntryPoint(HttpSecurity http) throws Exception {
        ExceptionHandlingConfigurer<HttpSecurity> exceptions = http.exceptionHandling();
        ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
        if (contentNegotiationStrategy == null) {
            contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
        }

        // 为Ajax指定EntryPoint
        exceptions.defaultAuthenticationEntryPointFor(
                new UnauthorizedEntryPoint(),
                new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
        // When multiple entry points are provided the default is the first one
    }


    /**
     * 是否开启csrf
     * @return
     */
    protected abstract boolean enableCsrf();

    /**
     * 是否开启cors
     * @return
     */
    protected abstract boolean enableCors();


    /**
     * 是否开启headers frameOptions
     * @return
     */
    protected abstract boolean enableFrameOptions();

    /**
     * 是否无状态
     * @return
     */
    protected abstract boolean isStateless();

    /**
     * 获取CorsConfigurationSource
     * @return
     */
    protected abstract CorsConfigurationSource corsConfigurationSource();



    /**
     * 留给子类实现的自定义HttpSecurity方法
     * @param http
     * @throws Exception
     */
    protected void customHttpSecurity(HttpSecurity http) throws Exception {
    }


    /**
     * 获取AuthorizeConfigManager
     * 具体的获取逻辑在子类实现
     */
    protected abstract AuthorizeConfigManager getAuthorizeConfigManager();

    /**
     * 获取PanGuHttpSecurityManager
     * 具体的获取逻辑在子类实现
     */
    protected abstract PanGuHttpSecurityManager getPanGuHttpSecurityManager();

    /**
     * 获取PanGuAuthenticationManagerBuilderManager
     * 具体的获取逻辑在子类实现
     */
    protected abstract PanGuAuthenticationManagerBuilderManager getPanGuAuthenticationManagerBuilderManager();


    protected abstract PasswordEncoder getPasswordEncoder();


    /**
     * 权限配置管理器
     * 移动到PanGuWebSecurityAutoConfiguration，以免某些情况下无法注册beanDefinition
     */
    //@Bean
    //public AuthorizeConfigManager authorizeConfigManager(){
    //    return new DefaultAuthorizeConfigManager();
    //}

    /**
     * HttpSecurity子配置管理器
     * 移动到PanGuWebSecurityAutoConfiguration，以免某些情况下无法注册beanDefinition
     */
    //@Bean
    //public PanGuHttpSecurityManager panGuHttpSecurityManager(){
    //    return new PanGuHttpSecurityManager();
    //}

}
